package com.example.java.base.concurrency.threadpool;

import com.example.java.base.concurrency.threadpool.factory.UserThreadFactory;
import com.example.java.base.concurrency.threadpool.task.DemoTask;
import com.example.java.base.concurrency.threadpool.task.ExecutorTask;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * A10UserThreadPool:自定义线程池
 *
 * @author zhangxiaoxiang
 * @date 2021/04/21
 */
@Slf4j
public class A10UserThreadPool {
    public static void main(String[] args) {
        //获取CPU核数
        final int nThreads = Runtime.getRuntime().availableProcessors();
        //默认拒绝策略是AbortPolicy, 抛出RejectedExecutionException拒绝任务的处理程序
        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                6,
                6,
                //当线程数大于内核数时,这是多余的空闲线程将在终止之前等待新任务的最长时间
                60L, TimeUnit.SECONDS,
                //这里为了演示效果,所以队列设置的很小,实际值是比较大的,根据业务实际设置
                // 对比一下LinkedBlockingQueue和ArrayBlockingQueue的区别
                // 1.底层数据结构不同
                // LinkedBlockingQueue底层是单向链表，只有一个后继指针 --- ArrayBlockingQueue底层是数组
                // 2.队列大小
                // LinkedBlockingQueue的构造函数，可以显示指定队列大小，也可以不指定，不指定大小事，默认是Integer.MAX_VALUE --- ArrayBlockingQueue的构造函数中，必须显示指定队列大小
                // 3.使用的锁不同
                // LinkedBlockingQueue中，有两个锁，都是非公平锁  --- ArrayBlockingQueue中，只有一个锁，默认是个非公平锁，也可以显示指定为公平锁
                new LinkedBlockingQueue<>(10),
                new UserThreadFactory("outbound"),
                //显示创建默认拒绝策略(尽量保证线程池的完整创建,有利于理解)
                new ThreadPoolExecutor.AbortPolicy());
        // new ThreadPoolExecutor.CallerRunsPolicy());
        //示例:默认核心线程6个,最大核心线程数也是6,队列10,那么同时处理的最大任务数是16,下面的for循环提交20个任务,就或超标,要么采用拒绝策略,要么加大线程或者队内容量
        //Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@7403c468*****
        //多线程下同时处理任务的数量区间[0,队列长度+maximunPoolSize],根据任务的处理,分CPU密集型(计算密集型)和IO密集型(数据库文件网络等操作),所以这里核心线程是和最大核心线程数取值就不一样
        //CPU型建议:corePollSize=系统核心数+1
        //IO型建议:corePollSize=系统核心数*2(其实这里是有一个公式的,实际中结合公式和压测最终综合评定)
        // for (int i = 0; i < 20; i++) {
        //     pool.submit(new ExecutorTask());
        // }
        for (int i = 0; i < 7; i++) {
            //7个任务完全可以执行,且有空闲
            pool.submit(new ExecutorTask());
        }
        log.info("1当前活跃线程数{},总任务数{},执行成功任务数{}", pool.getActiveCount(), pool.getTaskCount(), pool.getCompletedTaskCount());
        try {
            //测试keepAliveTime参数
            Thread.sleep(12000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("2当前活跃线程数{},总任务数{},执行成功任务数{}", pool.getActiveCount(), pool.getTaskCount(), pool.getCompletedTaskCount());
        //在上面完全执行的情况下,线程池还有空闲才可以提交任务
        for (int i = 0; i < 20; i++) {
            //超标的任务就丢弃
            pool.submit(new DemoTask());
        }
        log.info("3当前活跃线程数{},总任务数{},执行成功任务数{}", pool.getActiveCount(), pool.getTaskCount(), pool.getCompletedTaskCount());
        pool.shutdown();
    }

    // ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
    //         1,
    //         1,
    //         60L, TimeUnit.SECONDS,
    //         new LinkedBlockingQueue<>(5),
    //         new MyThreadFactory()
    // );


}
